package de.chessgame.tileentity;

import java.util.ArrayList;
import java.util.List;

import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.gameevent.InputEvent.KeyInputEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import de.chessgame.ChessGameMod;
import de.chessgame.block.BlockChessBoard;
import de.chessgame.gamelogic.Board;
import de.chessgame.gamelogic.Coordinate;
import de.chessgame.gamelogic.piece.Fraction;
import de.chessgame.gamelogic.piece.Piece;
import de.chessgame.gamelogic.piece.Type;
import de.chessgame.gamelogic.piece.UndefinedPiece;
import de.chessgame.gui.GuiChessBoard;
import de.chessgame.handler.ChessGameGuiHandler;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.command.PlayerNotFoundException;
import net.minecraft.entity.DataWatcher;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MathHelper;
import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.common.util.Constants;

public class TileEntityChessBoard extends TileEntity implements IInventory {

	public static final int PLAYER_WHITE = 0;
	public static final int PLAYER_BLACK = 1;

	private Board board;
	/** Player-Ids (now player names but will be updated to Ids in 1.8 **/
	private String playerOnWhite;
	private String playerOnBlack;
	private String playerOnWhiteLock;
	private String playerOnBlackLock;
	private static final int BOARD_SIZE = 8;
	private static final int INVENTORY_SIZE = BOARD_SIZE * BOARD_SIZE;

	public ArrayList<Coordinate> getHighlights() {
		return board.getHighlights();
	}

	public void calculateHighlights() {
		board.calculateHighlights();
	}

	public void removeHighlights() {
		board.removeHighlights();

	}

	public boolean isWhitePlayerOnTurn() {
		return board.isWhitePlayerOnTurn();
	}

	public String getPlayerOnWhite() {
		return playerOnWhite;
	}

	public String getPlayerOnBlack() {
		return playerOnBlack;
	}

	public int getWhiteDirection() {
		return BlockChessBoard.metadataToDirection(getBlockMetadata());
	}

	public void setPlayerOnWhiteLock(String playerOnWhiteLock) {
		this.playerOnWhiteLock = playerOnWhiteLock;
	}

	public void setPlayerOnBlackLock(String playerOnBlackLock) {
		this.playerOnBlackLock = playerOnBlackLock;
	}

	public boolean isWhiteSlotLocked() {
		return playerOnWhiteLock != null;
	}

	public boolean isBlackSlotLocked() {
		return playerOnBlackLock != null;
	}

	public String getPlayerOnWhiteLock() {
		return playerOnWhiteLock;
	}

	public String getPlayerOnBlackLock() {
		return playerOnBlackLock;
	}

	public boolean hasUndefined() {
		return board.hasUndefined();
	}

	public boolean isGameFinished() {
		if (board.isGameFinished()) {
			System.out.println("");
		}
		return board.isGameFinished();
	}

	public int getTypeOfEnding() {
		return board.getTypeOfEnding();
	}

	/**
	 * Constructor
	 */
	public TileEntityChessBoard() {
		this.board = new Board();
	}

	public void resetGame() {
		board.reset();
	}

	@Override
	public int getSizeInventory() {
		return INVENTORY_SIZE;
	}

	@Override
	public ItemStack getStackInSlot(int number) {
		Piece piece = getPieceInSlot(number);
		if (piece == null) {
			return null;
		} else {
			return new ItemStack(getItemFromPiece(piece), 1, 0);
		}
	}
	
	public Piece getPieceOnCoordinate(int x, int y) {
		return board.getPieceAt(x, y);
	}

	public Piece getPieceInSlot(int number) {
		int y = number / BOARD_SIZE;
		int x = number % BOARD_SIZE;
		return board.getPieceAt(x, y);
	}

	private void setPieceInSlot(int number, Piece piece) {
		int y = number / BOARD_SIZE;
		int x = number % BOARD_SIZE;
		board.setSlotContent(x, y, piece);
	}

	/**
	 * Called, when the player takes a Piece.
	 * **/
	@Override
	public ItemStack decrStackSize(int number, int amount) {
		if (amount == 0) {
			return null;
		}
		int y = number / BOARD_SIZE;
		int x = number % BOARD_SIZE;
		Piece piece = board.deletePieceAt(x, y);
		if (piece == null) {
			return null;
		} else {
			return new ItemStack(getItemFromPiece(piece), 1, 0);
		}
	}

	@Override
	public ItemStack getStackInSlotOnClosing(int slot) {
		return null;
	}

	/**
	 * Called, when the player puts a piece on a field.
	 */
	@Override
	public void setInventorySlotContents(int number, ItemStack itemStack) {
		Piece piece;
		if (itemStack != null) {
			piece = getPieceFromItem(itemStack.getItem());
		} else {
			piece = null;
		}
		setPieceInSlot(number, piece);
	}

	@Override
	public String getInventoryName() {
		return "gui.chessboard";
	}

	@Override
	public boolean hasCustomInventoryName() {
		return false;
	}

	@Override
	public int getInventoryStackLimit() {
		return 1;
	}

	@Override
	public boolean isUseableByPlayer(EntityPlayer player) {
		return isPlayerThePlayerInSlot(player);
	}

	public boolean isSlotEmptyForPlayer(EntityPlayer player) {
		if (didPlayerClickOnWhiteSide(player)) {
			return (playerOnWhiteLock == null && playerOnWhite == null)
					|| (player.getDisplayName().equals(playerOnWhiteLock));
		} else {
			return (playerOnBlackLock == null && playerOnBlack == null)
					|| (player.getDisplayName().equals(playerOnBlackLock));
		}
	}

	private boolean isPlayerThePlayerInSlot(EntityPlayer player) {
		if (didPlayerClickOnWhiteSide(player)) {
			return playerOnWhite != null
					&& playerOnWhite.equals(player.getDisplayName());
		} else {
			return playerOnBlack != null
					&& playerOnBlack.equals(player.getDisplayName());
		}
	}

	private boolean didPlayerClickOnWhiteSide(EntityPlayer player) {
		double direction = (double) (player.rotationYaw * 4.0F / 360.0F);

		while (direction >= 4) {
			direction -= 4;
		}

		while (direction < 0) {
			direction += 4;
		}

		int whiteDirection = getWhiteDirection();
		double min = whiteDirection - 1;
		min = min >= 4 ? min - 4 : min;
		min = min < 0 ? min + 4 : min;

		double max = whiteDirection + 1;
		max = max >= 4 ? max - 4 : max;
		max = max < 0 ? max + 4 : max;

		return (max < min && (direction >= min || direction < max))
				|| (direction >= min && direction < max);
	}

	@Override
	public void openInventory() {

	}

	@Override
	public void closeInventory() {
	}

	@Override
	public boolean isItemValidForSlot(int number, ItemStack itemStack) {
		Piece piece = getPieceFromItem(itemStack.getItem());
		return piece != null;
	}

	@Override
	public void writeToNBT(NBTTagCompound tagCompound) {
		super.writeToNBT(tagCompound);
		board.writeToNBT(tagCompound);

		NBTTagList tagList = new NBTTagList();

		if (playerOnWhiteLock != null) {
			NBTTagCompound tag = new NBTTagCompound();
			tag.setString("WhiteLock", playerOnWhiteLock);
			tagList.appendTag(tag);
		}

		if (playerOnBlackLock != null) {
			NBTTagCompound tag = new NBTTagCompound();
			tag.setString("BlackLock", playerOnBlackLock);
			tagList.appendTag(tag);
		}

		if (playerOnWhite != null) {
			NBTTagCompound tag = new NBTTagCompound();
			tag.setString("White", playerOnWhite);
			tagList.appendTag(tag);
		}

		if (playerOnBlack != null) {
			NBTTagCompound tag = new NBTTagCompound();
			tag.setString("Black", playerOnBlack);
			tagList.appendTag(tag);
		}

		tagCompound.setTag("Players", tagList);
	}

	@Override
	public void readFromNBT(NBTTagCompound tagCompound) {
		super.readFromNBT(tagCompound);
		board.readFromNBT(tagCompound);

		NBTTagList tagList = tagCompound.getTagList("Players",
				Constants.NBT.TAG_COMPOUND);

		boolean white = false;
		boolean black = false;
		boolean whiteLock = false;
		boolean blackLock = false;

		for (int i = 0; i < tagList.tagCount(); i++) {
			NBTTagCompound tag = tagList.getCompoundTagAt(i);

			if (tag.hasKey("WhiteLock")) {
				playerOnWhiteLock = tag.getString("WhiteLock");
				whiteLock = true;
			} else if (tag.hasKey("BlackLock")) {
				playerOnBlackLock = tag.getString("BlackLock");
				blackLock = true;
			} else if (tag.hasKey("White")) {
				playerOnWhite = tag.getString("White");
				white = true;
			} else if (tag.hasKey("Black")) {
				playerOnBlack = tag.getString("Black");
				black = true;
			}
		}

		if (!whiteLock) {
			playerOnWhiteLock = null;
		}

		if (!blackLock) {
			playerOnBlackLock = null;
		}

		if (!white) {
			playerOnWhite = null;
		}

		if (!black) {
			playerOnBlack = null;
		}
	}

	@Override
	public Packet getDescriptionPacket() {
		NBTTagCompound tagCompound = new NBTTagCompound();
		writeToNBT(tagCompound);
		return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord,
				this.zCoord, 2, tagCompound);
	}

	@Override
	public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
		readFromNBT(pkt.func_148857_g());
	};

	public void onPlayerClick(EntityPlayer entityPlayer) {
		if (didPlayerClickOnWhiteSide(entityPlayer) && playerOnWhite == null) {
			playerOnWhite = entityPlayer.getDisplayName();
		} else if (!didPlayerClickOnWhiteSide(entityPlayer)
				&& playerOnBlack == null) {
			playerOnBlack = entityPlayer.getDisplayName();
		}
	}

	public boolean isSlotUsableByPlayer(int number, EntityPlayer player) {
		boolean white = player.getDisplayName().equals(playerOnWhite);
		boolean black = player.getDisplayName().equals(playerOnBlack);
		int y = number / BOARD_SIZE;
		int x = number % BOARD_SIZE;
		Piece piece = board.getPieceAt(x, y);

		if (piece == null) {
			return true;
		}

		if (white) {
			if (!board.isWhitePlayerOnTurn()) {
				return false;
			}
			if (piece.getFraction() == Fraction.WHITE) {
				return true;
			}
			return false;
		} else if (black) {
			if (board.isWhitePlayerOnTurn()) {
				return false;
			}
			if (piece.getFraction() == Fraction.BLACK) {
				return true;
			}
			return false;
		}
		return false;
	}

	public static Item getItemFromPiece(Piece piece) {
		if (piece == null) {
			return null;
		}
		Type type = piece.getType();
		Fraction fraction = piece.getFraction();

		if (type == null && fraction == null) {
			return null;
		}

		if (type == null) {
			return Items.snowball;
		}

		if (fraction == Fraction.WHITE) {
			switch (type) {
			case KING:
				return ChessGameMod.itemKingWhite;

			case QUEEN:
				return ChessGameMod.itemQueenWhite;

			case ROOK:
				return ChessGameMod.itemRookWhite;

			case KNIGHT:
				return ChessGameMod.itemKnightWhite;

			case BISHOP:
				return ChessGameMod.itemBishopWhite;

			case PAWN:
				return ChessGameMod.itemPawnWhite;

			default:
				return null;
			}
		} else {
			switch (type) {
			case KING:
				return ChessGameMod.itemKingBlack;

			case QUEEN:
				return ChessGameMod.itemQueenBlack;

			case ROOK:
				return ChessGameMod.itemRookBlack;

			case KNIGHT:
				return ChessGameMod.itemKnightBlack;

			case BISHOP:
				return ChessGameMod.itemBishopBlack;

			case PAWN:
				return ChessGameMod.itemPawnBlack;

			default:
				return null;
			}
		}
	}

	public static Piece getPieceFromItem(Item item) {
		if (item == null) {
			return null;
		}
		Type type = null;
		Fraction fraction = null;

		Type king = Type.KING;
		Type queen = Type.QUEEN;
		Type rook = Type.ROOK;
		Type knight = Type.KNIGHT;
		Type bishop = Type.BISHOP;
		Type pawn = Type.PAWN;

		Fraction white = Fraction.WHITE;
		Fraction black = Fraction.BLACK;

		if (item == ChessGameMod.itemKingWhite) {
			type = king;
			fraction = white;
		} else if (item == ChessGameMod.itemQueenWhite) {
			type = queen;
			fraction = white;
		} else if (item == ChessGameMod.itemRookWhite) {
			type = rook;
			fraction = white;
		} else if (item == ChessGameMod.itemKnightWhite) {
			type = knight;
			fraction = white;
		} else if (item == ChessGameMod.itemBishopWhite) {
			type = bishop;
			fraction = white;
		} else if (item == ChessGameMod.itemPawnWhite) {
			type = pawn;
			fraction = white;
		} else if (item == ChessGameMod.itemKingBlack) {
			type = king;
			fraction = black;
		} else if (item == ChessGameMod.itemQueenBlack) {
			type = queen;
			fraction = black;
		} else if (item == ChessGameMod.itemRookBlack) {
			type = rook;
			fraction = black;
		} else if (item == ChessGameMod.itemKnightBlack) {
			type = knight;
			fraction = black;
		} else if (item == ChessGameMod.itemBishopBlack) {
			type = bishop;
			fraction = black;
		} else if (item == ChessGameMod.itemPawnBlack) {
			type = pawn;
			fraction = black;
		} else {
			return null;
		}
		return Piece.createPiece(type, fraction);
	}

	public void onGuiClosed(EntityPlayer player) {
		board.resetPieceForTurn();
		removePlayer(player);
	}

	private void removePlayer(EntityPlayer player) {
		if (playerOnWhite != null
				&& playerOnWhite.equals(player.getDisplayName())) {
			playerOnWhite = null;
		} else if (playerOnBlack != null
				&& playerOnBlack.equals(player.getDisplayName())) {
			playerOnBlack = null;
		}
	}

	public static int getSlotIdFromCoordinate(int x, int y) {
		return BOARD_SIZE * y + x;
	}

	public void setUndefinedAsQueen() {
		board.setUndefinedAsPiece(Type.QUEEN);

	}

	public void setUndefinedAsRook() {
		board.setUndefinedAsPiece(Type.ROOK);

	}

	public void setUndefinedAsKnight() {
		board.setUndefinedAsPiece(Type.KNIGHT);
	}

	public void setUndefinedAsBishop() {
		board.setUndefinedAsPiece(Type.BISHOP);
	}

}
